// Make sure you don't get warnings about tabsize
#pragma tabsize 0

// Include default files
#include <a_samp>
#include <sscanf2>
#include <streamer>
#include <zcmd>



// ******************************************************************************************************************************
// Settings that can be changed
// ******************************************************************************************************************************

// Define where the Camera-file is located (all camera's are stored in one file) and the max amount of speedcamera's
#define CameraFile		"PPC_Speedometer/Cameras.ini"
#define MAX_CAMERAS		100



// Define maximum fuel amount (default: 2400)
// Changing MaxFuel changes how fast a vehicle will run without fuel
// 1 fuel is consumed every half a second, that's 2 fuel per second
// A value of 2400 allows you to drive 2400/2 = 1200 seconds (or 1200/60 = 20 minutes) without refuelling
new MaxFuel = 2400;
// RefuelMaxPrice is the price you pay for a total refuel (when the vehicle has no more fuel), the price to pay is calculated
//      by the amount of fuel to refuel (pay 50% of RefuelMaxPrice when vehicle has half a fuel-tank left)
new RefuelMaxPrice = 1000;

// An extra setting to disable the shadows for the speedometer and fuel textdraws (shadows enabled by default)
new bool:DisableShadows = false;



// ******************************************************************************************************************************
// Enums and the array-setups that use them
// ******************************************************************************************************************************

// Setup a custom type that holds all data about a speedcamera
enum TSpeedCamera
{
	Float:CamX, // Holds the X-coordinate of the camera
	Float:CamY, // Holds the Y-coordinate of the camera
	Float:CamZ, // Holds the Z-coordinate of the camera
	Float:CamAngle, // Holds the Angle of the camera
	CamSpeed, // Holds the maximum speed allowed to pass this camera without being caught
	CamObj1, // Holds the reference to the first camera object
	CamObj2 // Holds the reference to the second camera object
}
new ACameras[MAX_CAMERAS][TSpeedCamera];

// Setup a custom type that holds the data of pickups
enum TPickupData
{
	Float:pux, // Holds the x-position of the refuel-pickup
	Float:puy, // Holds the y-position of the refuel-pickup
	Float:puz, // Holds the z-position of the refuel-pickup
	PickupID // Holds the PickupID of the refuel-pickup
}
// Holds the data for pickups for refuelling (maximum 50 refuel-pickups)
new ARefuelPickups[50][TPickupData];

// Setup a custom type to hold all data about a vehicle
enum TVehicleData
{
	Fuel // Holds the amount of fuel for this vehicle
}
// Setup an array which holds all data for every vehicleid, max 2000 vehicles (server limit)
new AVehicleData[2000][TVehicleData];

// Setup all the fields required for the player data (Speedometer TextDraw, current job, ...)
enum TPlayerData
{
	Text:SpeedometerText, // The TextDraw of the speedometer for this player
	Text:FuelGauge, // The textdraw of the fuel-gauge for this player
	SpeedometerTimer, // Holds the reference to the speedometer timer for this player
	PlayerSpeed, // Holds the speed of the player
	PlayerCaughtSpeeding // This holds a value to prevent being caught multiple times by the same speedcamera
}
// Create an array to hold the playerdata for every player
new APlayerData[MAX_PLAYERS][TPlayerData];



// These variables are used when starting the script and debugging purposes
new TotalRefuelStations, TotalCameras;



// ******************************************************************************************************************************
// Callbacks
// ******************************************************************************************************************************

// The main function (used only once when the server loads)
main()
{
}

// This callback gets called when the server initializes the filterscript
public OnFilterScriptInit()
{
	// Loop through all vehicles
    for (new vid; vid < 2000; vid++)
	{
		// If this vehicle belongs to the PPC_Housing script, don't refuel it, as the housing script manages the fuel for this vehicle
		// by using remote calls to this script
		if (CallRemoteFunction("Housing_IsVehicleOwned", "i", vid) == 1)
		{
		    // The vehicle is owned by a player in the housing script, so do nothing, the housing script manages this vehicle's fuel
		}
		else // The vehicle doesn't belong to the housing script (it's created and managed by another script), set the fuel to maximum
	        AVehicleData[vid][Fuel] = MaxFuel;
	}

	// Add all refuel-pickups to the world (including their icon)
	AddRefuelPickup(-1471.5, 1863.75, 32.7);
	AddRefuelPickup(-1326.5, 2677.5, 50.1);
	AddRefuelPickup(611.5, 1694.5, 7.0);
	AddRefuelPickup(-2249.25, -2559.0, 32.0);
	AddRefuelPickup(-1606.5, -2714.0, 48.6);
	AddRefuelPickup(-93.5, -1175.0, 2.3);
	AddRefuelPickup(1377.5, 457.0, 19.9);
	AddRefuelPickup(651.5, -565.5, 16.4);
	AddRefuelPickup(-1675.75, 412.75, 7.2);
	AddRefuelPickup(-2405.50, 976.25, 45.3);
	AddRefuelPickup(-2023.25, 156.75, 28.9);
	AddRefuelPickup(-1131.75, -204.25, 14.2);
	AddRefuelPickup(66.50, 1220.50, 18.9);
	AddRefuelPickup(350.50, 2537.50, 16.8);
	AddRefuelPickup(2147.00, 2747.75, 10.9);
	AddRefuelPickup(2639.75, 1106.00, 10.9);
	AddRefuelPickup(2115.00, 920.00, 10.9);
	AddRefuelPickup(2202.00, 2475.00, 10.9);
	AddRefuelPickup(1596.50, 2199.75, 10.9);
	AddRefuelPickup(1584.25, 1448.25, 10.9);
	AddRefuelPickup(1004.25, -940.50, 42.2);
	AddRefuelPickup(1935.00, -1772.75, 13.4);

	// Load all speedcamera's
	CameraFile_Load();

    printf("\n----------------------------------------");
    printf("PPC Speedometer filterscript initialized");
    printf("Gas-stations created: %i", TotalRefuelStations);
    printf("Speedcamera's loaded: %i", TotalCameras);
    printf("----------------------------------------\n");

    return 1;
}

// This callback gets called when a player connects to the server
public OnPlayerConnect(playerid)
{
	// Setup the speedometer for this player
	Speedometer_Setup(playerid);

	return 1;
}

// This callback gets called when a player disconnects from the server
public OnPlayerDisconnect(playerid, reason)
{
	// Cleanup the speedometer for this player
	Speedometer_Cleanup(playerid);

	return 1;
}

// This callback gets called when a vehicle respawns at it's spawn-location (where it was created)
public OnVehicleSpawn(vehicleid)
{
	// If this vehicle belongs to the PPC_Housing script, don't refuel it, as the housing script manages the fuel for this vehicle
	// by using remote calls to this script
	if (CallRemoteFunction("Housing_IsVehicleOwned", "i", vehicleid) == 1)
	{
	    // The vehicle is owned by a player in the housing script, so do nothing, the housing script manages this vehicle's fuel
	}
	else // The vehicle doesn't belong to the housing script (it's created and managed by another script), set the fuel to maximum
        AVehicleData[vehicleid][Fuel] = MaxFuel;

    return 1;
}

// This callback gets called whenever a player changes state
public OnPlayerStateChange(playerid,newstate,oldstate)
{
	// Setup local variables
	new vehicleid, engine, lights, alarm, doors, bonnet, boot, objective;

	// Check if the player entered a vehicle as driver
	if (newstate == PLAYER_STATE_DRIVER)
	{
	    // Get the player's vehicle
	    vehicleid = GetPlayerVehicleID(playerid);

		// Check if the vehicle has fuel
		if (AVehicleData[vehicleid][Fuel] > 0)
		{
			// Start the engine and turn on the lights
			GetVehicleParamsEx(vehicleid, engine, lights, alarm, doors, bonnet, boot, objective);
			SetVehicleParamsEx(vehicleid, 1, 1, alarm, doors, bonnet, boot, objective);
		}
	}
}

// This callback gets called when a player exits his vehicle
public OnPlayerExitVehicle(playerid, vehicleid)
{
	// Setup local variables
	new engine, lights, alarm, doors, bonnet, boot, objective;

	// Check if the player is the driver of the vehicle
	if (GetPlayerVehicleSeat(playerid) == 0)
	{
		// Turn off the lights and engine
		GetVehicleParamsEx(vehicleid, engine, lights, alarm, doors, bonnet, boot, objective);
		SetVehicleParamsEx(vehicleid, 0, 0, alarm, doors, bonnet, boot, objective);
	}

	return 1;
}

// This callback gets called whenever a player presses a key
public OnPlayerKeyStateChange(playerid, newkeys, oldkeys)
{
	// Refuel a vehicle when driving a vehicle and pressing the HORN key near a refuelpickup
	// Check if the player presses the HORN key
	if ((newkeys & KEY_CROUCH) && !(oldkeys & KEY_CROUCH))
	{
		// Check if the player is driving a vehicle
		if (GetPlayerVehicleSeat(playerid) == 0)
		{
			// Loop through all ARefuelPickups
			for (new i; i < sizeof(ARefuelPickups); i++)
			{
			    // Check if this refuel-pickup exists (check for a valid pickup)
				if (IsValidDynamicPickup(ARefuelPickups[i][PickupID]))
			    {
					// Check if the player is in range of a refuelpickup
					if(IsPlayerInRangeOfPoint(playerid, 5.0, ARefuelPickups[i][pux], ARefuelPickups[i][puy], ARefuelPickups[i][puz]))
					{
						// Show a message that the player's vehicle is refuelling
						GameTextForPlayer(playerid, "~g~Refuelling...", 3000, 4);
						// Don't allow the player to move again (the timer will allow it after refuelling)
						TogglePlayerControllable(playerid, 0);
						// Start a timer (let the player wait until the vehicle is refuelled)
					    SetTimerEx("RefuelVehicle", 5000, false, "i", playerid);
					    // Stop the search
						break;
					}
				}
				else // No more refuel-pickups, so stop searching through the array
				    break;
			}
		}
	}
}



// ******************************************************************************************************************************
// Commands
// ******************************************************************************************************************************

// This command allows you to create a speedcamera
COMMAND:createcamera(playerid, params[])
{
	// Setup local variables
	new Float:x, Float:y, Float:z, Float:rot, MaxSpeed, Msg[128];

	// If a player hasn't logged in properly, he cannot use this command
	if (INT_IsPlayerLoggedIn(playerid) == 0) return 0;
	// If the player has an insufficient admin-level (he needs level 5 or RCON admin), exit the command
	// returning "SERVER: Unknown command" to the player
	if (INT_CheckPlayerAdminLevel(playerid, 5) == 0) return 0;

	// Split the parameters to useable variables
	if (sscanf(params, "i", MaxSpeed)) SendClientMessage(playerid, 0xFFFFFFFF, "{FF0000}Usage: \"/createcamera <max_speed>\"");
	else
	{
		// Check if the player is on foot
		if (GetPlayerVehicleSeat(playerid) == -1)
		{
			// Get player's position and facing angle
			GetPlayerPos(playerid, x, y, z);
			GetPlayerFacingAngle(playerid, rot);
			z = z - 1.0; // Adjust camera Z-coordinate 1m lower than normal (otherwise the camera floats in the air)

			// Move the player a bit, otherwise he could get stuck inside the camera-object
			SetPlayerPos(playerid, x, y + 1.0, z + 1.0);

			// Save the camera to a file
			for (new CamID; CamID < MAX_CAMERAS; CamID++)
			{
				// Check if this index is free
				if (ACameras[CamID][CamSpeed] == 0)
				{
				    // Setup this camera (create the objects and store the data)
					SetupSpeedCamera(CamID, x, y, z, rot, MaxSpeed);

					// Also save the entire camerafile again to add this new camera to the file
					CameraFile_Save();

					// Let the player know he created a new camera
					format(Msg, 128, "{00FF00}You've created a speed-camera with ID: {FFFF00}%i", CamID);
					SendClientMessage(playerid, 0xFFFFFFFF, Msg);

					// Exit the function
					return 1;
				}
			}

			// In case all camera-slots are occupied (100 camera's have been created already), let the player know about it
			format(Msg, 128, "{FF0000}You cannot create more than %i speedcamera's", MAX_CAMERAS);
			SendClientMessage(playerid, 0xFFFFFFFF, Msg);
		}
		else
			SendClientMessage(playerid, 0xFFFFFFFF, "{FF0000}You must be on foot to use this command");
	}

	// Let the server know that this was a valid command
	return 1;
}

// This command allows you to delete a speedcamera
COMMAND:delcamera(playerid, params[])
{
	// Setup local variables
	new Msg[128];

	// If a player hasn't logged in properly, he cannot use this command
	if (INT_IsPlayerLoggedIn(playerid) == 0) return 0;
	// If the player has an insufficient admin-level (he needs level 5 or RCON admin), exit the command
	// returning "SERVER: Unknown command" to the player
	if (INT_CheckPlayerAdminLevel(playerid, 5) == 0) return 0;

	// Check if the player is on foot
	if (GetPlayerVehicleSeat(playerid) == -1)
	{
		// Loop through all camera's
		for (new CamID; CamID < MAX_CAMERAS; CamID++)
		{
			// Check if this index is used
			if (ACameras[CamID][CamSpeed] != 0)
			{
				// Check if the player is in range of the camera
				if (IsPlayerInRangeOfPoint(playerid, 5.0, ACameras[CamID][CamX], ACameras[CamID][CamY], ACameras[CamID][CamZ]))
				{
					// Delete both camera objects
				    DestroyDynamicObject(ACameras[CamID][CamObj1]);
				    DestroyDynamicObject(ACameras[CamID][CamObj2]);
					// Also clear the data from memory
				    ACameras[CamID][CamX] = 0.0;
				    ACameras[CamID][CamY] = 0.0;
				    ACameras[CamID][CamZ] = 0.0;
				    ACameras[CamID][CamAngle] = 0.0;
				    ACameras[CamID][CamSpeed] = 0;
	                ACameras[CamID][CamObj1] = 0;
	                ACameras[CamID][CamObj2] = 0;

					// Also save the entire camerafile again to remove this camera from the file
					CameraFile_Save();

					// Let the player know he deleted a camera
					format(Msg, 128, "{00FF00}You've deleted speed-camera {FFFF00}%i", CamID);
					SendClientMessage(playerid, 0xFFFFFFFF, Msg);

					// Exit the function
					return 1;
				}
			}
		}

		// In case the player wasn't near a speedcamera, inform him about it
		SendClientMessage(playerid, 0xFFFFFFFF, "{FF0000}You must be near a speedcamera to delete it");
	}
	else
		SendClientMessage(playerid, 0xFFFFFFFF, "{FF0000}You must be on foot to use this command");

	// Let the server know that this was a valid command
	return 1;
}

// This command allows you to refuel your vehicle for free (admins only)
COMMAND:fuel(playerid, params[])
{
	// Setup local variables
	new vid, engine, lights, alarm, doors, bonnet, boot, objective;

	// If a player hasn't logged in properly, he cannot use this command
	if (INT_IsPlayerLoggedIn(playerid) == 0) return 0;
	// If the player has an insufficient admin-level (he needs level 1 or RCON admin), exit the command
	// returning "SERVER: Unknown command" to the player
	if (INT_CheckPlayerAdminLevel(playerid, 1) == 0) return 0;

	// Check if the player is the driver of a vehicle
	if (GetPlayerVehicleSeat(playerid) == 0)
	{
	    // Get the vehicleid
	    vid = GetPlayerVehicleID(playerid);
	    // Refuel the vehicle
	    AVehicleData[vid][Fuel] = MaxFuel;
		// Also (re-)start the engine and turn on the lights in case the vehicle is completely out of fuel
		GetVehicleParamsEx(vid, engine, lights, alarm, doors, bonnet, boot, objective);
		SetVehicleParamsEx(vid, 1, 1, alarm, doors, bonnet, boot, objective);
		// Let the player know about it
		SendClientMessage(playerid, 0xFFFFFFFF, "{00FF00}Your vehicle is refuelled");
	}
	else
		SendClientMessage(playerid, 0xFFFFFFFF, "{FF0000}You're not driving a vehicle");

	// Let the server know that this was a valid command
	return 1;
}



// ******************************************************************************************************************************
// Speedometer functions
// ******************************************************************************************************************************

// This function sets up the speedometer for the given player
Speedometer_Setup(playerid)
{
	// Setup the speedometer for the player
	APlayerData[playerid][SpeedometerText] = TextDrawCreate(500.0, 395.0, " ");
	APlayerData[playerid][FuelGauge] = TextDrawCreate(500.0, 410.0, " ");
	// Disable shadows if required
	if (DisableShadows == true)
	{
		TextDrawSetShadow(APlayerData[playerid][SpeedometerText], 0);
		TextDrawSetShadow(APlayerData[playerid][FuelGauge], 0);
	}
	// Enable the TextDraw for this player
	TextDrawShowForPlayer(playerid, APlayerData[playerid][SpeedometerText]);
	TextDrawShowForPlayer(playerid, APlayerData[playerid][FuelGauge]);

	// Start the speedometer timer for this player
	APlayerData[playerid][SpeedometerTimer] = SetTimerEx("Speedometer_Update", 500, true, "i", playerid);

	return 1;
}

// This function cleans up the speedometer for the given player
Speedometer_Cleanup(playerid)
{
	// Destroy the speedometer textdraw
	TextDrawDestroy(APlayerData[playerid][SpeedometerText]);
	TextDrawDestroy(APlayerData[playerid][FuelGauge]);
	// Kill the speedometer timer
	KillTimer(APlayerData[playerid][SpeedometerTimer]);
	// Set player speed to 0
	APlayerData[playerid][PlayerSpeed] = 0;

	return 1;
}

// Forward the function needed to update the speedometer
forward Speedometer_Update(playerid);
// This function gets called by a timer which runs every 500ms to display and update the speedometer
public Speedometer_Update(playerid)
{
	// Setup local variables
	new vid, Float:speed_x, Float:speed_y, Float:speed_z, Float:final_speed, speed_string[50], final_speed_int, Float:vehiclehealth;
	new FuelString[50], FuelStatus[20];

	// Get the ID of the player's vehicle
	vid = GetPlayerVehicleID(playerid);

	// If the player is inside a vehicle
	if(vid != 0)
	{
		// Get the vehicles velocity
		GetVehicleVelocity(vid, speed_x, speed_y, speed_z);
		// Calculate the speed (in kph)
		final_speed = floatsqroot(((speed_x * speed_x) + (speed_y * speed_y)) + (speed_z * speed_z)) * 158.179;
		// Convert the float value to an int value
		final_speed_int = floatround(final_speed, floatround_round);
		// Also save the speed for the player
		APlayerData[playerid][PlayerSpeed] = final_speed_int;
		// Setup the string to display for the player and display it
		format(speed_string, 50, "~w~Speed: ~b~%i~w~ kph", final_speed_int);
		TextDrawSetString(APlayerData[playerid][SpeedometerText], speed_string);

		// Also display the vehicle's health through the player-health bar
		GetVehicleHealth(vid, vehiclehealth);
		SetPlayerHealth(vid, vehiclehealth / 10.0);

		// Check if the player is the driver of the vehicle (otherwise every passenger would consume fuel for just being in the car)
		if (GetPlayerVehicleSeat(playerid) == 0)
			if ((final_speed_int > 10) && (AVehicleData[vid][Fuel] > 0)) // Check if the speed is above 10kph and if the vehicle didn't run out of fuel
				AVehicleData[vid][Fuel] = AVehicleData[vid][Fuel] - 1; // Decrease the fuel for this vehicle every time the timer is run

		// Construct the fuelgauge
		if ((AVehicleData[vid][Fuel] > 0) && (AVehicleData[vid][Fuel] < 100000))
			format(FuelStatus, 20, "~g~%s~r~%s", "I", "IIIIIIIII"); // Fuel is between 0% and 10% full

		if ((AVehicleData[vid][Fuel] >= ((MaxFuel / 10) * 1)) && (AVehicleData[vid][Fuel] < ((MaxFuel / 10) * 2)))
			format(FuelStatus, 20, "~g~%s~r~%s", "II", "IIIIIIII"); // Fuel is between 10% and 20% full

		if ((AVehicleData[vid][Fuel] >= ((MaxFuel / 10) * 2)) && (AVehicleData[vid][Fuel] < ((MaxFuel / 10) * 3)))
			format(FuelStatus, 20, "~g~%s~r~%s", "III", "IIIIIII"); // Fuel is between 20% and 30% full

		if ((AVehicleData[vid][Fuel] >= ((MaxFuel / 10) * 3)) && (AVehicleData[vid][Fuel] < ((MaxFuel / 10) * 4)))
			format(FuelStatus, 20, "~g~%s~r~%s", "IIII", "IIIIII"); // Fuel is between 30% and 40% full

		if ((AVehicleData[vid][Fuel] >= ((MaxFuel / 10) * 4)) && (AVehicleData[vid][Fuel] < ((MaxFuel / 10) * 5)))
			format(FuelStatus, 20, "~g~%s~r~%s", "IIIII", "IIIII"); // Fuel is between 40% and 50% full

		if ((AVehicleData[vid][Fuel] >= ((MaxFuel / 10) * 5)) && (AVehicleData[vid][Fuel] < ((MaxFuel / 10) * 6)))
			format(FuelStatus, 20, "~g~%s~r~%s", "IIIIII", "IIII"); // Fuel is between 50% and 60% full

		if ((AVehicleData[vid][Fuel] >= ((MaxFuel / 10) * 6)) && (AVehicleData[vid][Fuel] < ((MaxFuel / 10) * 7)))
			format(FuelStatus, 20, "~g~%s~r~%s", "IIIIIII", "III"); // Fuel is between 60% and 70% full

		if ((AVehicleData[vid][Fuel] >= ((MaxFuel / 10) * 7)) && (AVehicleData[vid][Fuel] < ((MaxFuel / 10) * 8)))
			format(FuelStatus, 20, "~g~%s~r~%s", "IIIIIIII", "II"); // Fuel is between 70% and 80% full

		if ((AVehicleData[vid][Fuel] >= ((MaxFuel / 10) * 8)) && (AVehicleData[vid][Fuel] < ((MaxFuel / 10) * 9)))
			format(FuelStatus, 20, "~g~%s~r~%s", "IIIIIIIII", "I"); // Fuel is between 80% and 90% full

		if ((AVehicleData[vid][Fuel] >= ((MaxFuel / 10) * 9)) && (AVehicleData[vid][Fuel] <= MaxFuel))
			format(FuelStatus, 20, "~g~%s", "IIIIIIIIII"); // Fuel is between 90% and 100% full (all bars are green)

		if (AVehicleData[vid][Fuel] == 0)
			format(FuelStatus, 20, "~r~%s", "IIIIIIIIII"); // Fuel is empty (all bars are red)

		// Format the final fuel-gauge readout
		format(FuelString, 50, "~w~Fuel: %s", FuelStatus);
		// Display the fuel-gauge
		TextDrawSetString(APlayerData[playerid][FuelGauge], FuelString);

		// Check if the vehicle is out of fuel
		if (AVehicleData[vid][Fuel] == 0)
		{
			// Stop the engine and turn off the lights so the player cannot drive anymore with this vehicle
			new engine,lights,alarm,doors,bonnet,boot,objective;
			GetVehicleParamsEx(vid, engine, lights, alarm, doors, bonnet, boot, objective);
			SetVehicleParamsEx(vid, 0, 0, alarm, doors, bonnet, boot, objective);
		}

		// Check if the player is not in any plane or helicopter (those cannot be caught by speedcamera's)
		if (IsVehicleAirVehicle(vid) == 0)
			CheckPlayerSpeeding(playerid);
	}
	else
	{
		// If the player is not inside a vehicle, display an empty string (looks like the speedometer is gone)
		TextDrawSetString(APlayerData[playerid][SpeedometerText], " ");
		TextDrawSetString(APlayerData[playerid][FuelGauge], " ");
		// Set the speed of the player to 0
		APlayerData[playerid][PlayerSpeed] = 0;
	}
}

// This function checks if the player is speeding near a speedcamera
CheckPlayerSpeeding(playerid)
{
	// Check if the player hasn't been caught speeding recently
	if (APlayerData[playerid][PlayerCaughtSpeeding] == 0)
	{
		// Loop through all speedcameras
		for (new CamID; CamID < MAX_CAMERAS; CamID++)
		{
		    // Check if this camera has been created
		    if (ACameras[CamID][CamSpeed] != 0)
		    {
				// Check if the player is the driver of the vehicle
				if (GetPlayerVehicleSeat(playerid) == 0)
				{
					// Check if the player's speed is greater than the speed allowed by this camera (no need to process a distance-check if not speeding)
					if (APlayerData[playerid][PlayerSpeed] > ACameras[CamID][CamSpeed])
					{
						// Check if the player is near the camera
						if (IsPlayerInRangeOfPoint(playerid, 50.0, ACameras[CamID][CamX], ACameras[CamID][CamY], ACameras[CamID][CamZ]))
						{
						    // Prevent the player being caught multiple times by the same speed-camera
						    APlayerData[playerid][PlayerCaughtSpeeding] = 20;
						    // Increase the wanted-level of this player by 1 star
						    SetPlayerWantedLevel(playerid, GetPlayerWantedLevel(playerid) + 1);
						    // Let the player know he's been caught speeding
						    SendClientMessage(playerid, 0xFFFFFFFF, "{FF0000}You've been caught by a speedtrap, slow down!");
						}
					}
				}
		    }
		}
	}
	else // If the player has been caught before, reduce the value until it's 0 again, then he can be caught again
	    APlayerData[playerid][PlayerCaughtSpeeding]--;
}

// This function returns "1" if the given vehicle-id is a plane or helicopter
IsVehicleAirVehicle(vid)
{
	switch (GetVehicleModel(vid))
	{
		case 592, 577, 511, 512, 593, 520, 553, 476, 519, 460, 513, 548, 425, 417, 487, 488, 497, 563, 447, 469: return 1;
		default: return 0;
	}

	return 0;
}

// Forward the refueltimer
forward RefuelVehicle(playerid);
// This timer-function is called when a player refuels near a refuelpickup
public RefuelVehicle(playerid)
{
	new RefuelMsg[128];
	// Get the vehicle-id of the player's vehicle
	new vid = GetPlayerVehicleID(playerid);
	// Calculate the amount of fuel that needs to be refuelled
	new Amount = MaxFuel - AVehicleData[vid][Fuel];
	// Calculate the price to refuel
	new RefuelPrice = (Amount * RefuelMaxPrice) / MaxFuel;

	// Check if the player has enough cash
	if (INT_GetPlayerMoney(playerid) >= RefuelPrice)
	{
		// Refuel the vehicle
		AVehicleData[vid][Fuel] = MaxFuel;
		// Withdraw the money from the player
		INT_GivePlayerMoney(playerid, -RefuelPrice);
		// Let the player know he refuelled his vehicle
		format(RefuelMsg, 128, "{00FF00}You refuelled your vehicle for $%i", RefuelPrice);
		SendClientMessage(playerid, 0xFFFFFFFF, RefuelMsg);
	}
	else
	    SendClientMessage(playerid, 0xFFFFFFFF, "{FF0000}You don't have enough cash to refuel your vehicle");

	// Allow the player to move again
	TogglePlayerControllable(playerid, 1);

	return 1;
}

// This function is used to add refuelling pickups to the map
AddRefuelPickup(Float:x, Float:y, Float:z)
{
	// Add the pickup-id to the ARefuelPickups array
	for (new i; i < sizeof(ARefuelPickups); i++)
	{
		// If an empty array-index is found (no valid pickup)
		if (!IsValidDynamicPickup(ARefuelPickups[i][PickupID]))
		{
		    // Store the pickup-id in this empty slot
			ARefuelPickups[i][PickupID] = CreateDynamicPickup(1244, 1, x, y, z, 0); // Type 1, cannot be pickup up, exists all the time
			ARefuelPickups[i][pux] = x;
			ARefuelPickups[i][puy] = y;
			ARefuelPickups[i][puz] = z;
			// Count the total amount of refuelstations that have been created
			TotalRefuelStations++;

			// Add a 3DText message above the refuel-pickup
			CreateDynamic3DTextLabel("Honk the horn\nto refuel your vehicle", 0x008080FF, x, y, z + 0.8, 30.0);

			// Add an icon to the map for this refuel-spot
			CreateDynamicMapIcon(x, y, z, 56, 0, 0, 0, -1, 300.0);

			// Stop browsing through the array
			break;
		}
	}
}

// This function will load the speedcamera's datafile (used when the server is started to load all cameras)
CameraFile_Load()
{
	// Setup local variables
	new file[128], File:CFile, LineFromFile[100], ParameterName[50], ParameterValue[50];
	new CamID, Float:x, Float:y, Float:z, Float:rot, MaxSpeed;

    // Construct the complete filename for this camera-file
	format(file, sizeof(file), CameraFile);

	// Check if the camerafile exists
	if (fexist(file))
	{
	    // Open the camerafile for reading
		CFile = fopen(file, io_read);

        // Read the first line of the file
		fread(CFile, LineFromFile);

		// Keep reading until the end of the file is found (no more data)
		// An empty line between data-segments still has the NewLine characters (\r\n) so it's not completely empty
		// Reading past the last line will read a completely empty line, therefore indicating the end of the file
		while (strlen(LineFromFile) > 0)
		{
			StripNewLine(LineFromFile); // Strip any newline characters from the LineFromFile
			sscanf(LineFromFile, "s[50]s[50]", ParameterName, ParameterValue); // Extract parametername and parametervalue

			// Check if there is anything in the LineFromFile (skipping empty lines)
			if (strlen(LineFromFile) > 0)
			{
				// Check if a header for a camera has been found
				if (strcmp(ParameterName, "[Camera]", false) == 0)
				{
				    // Clear all variables
				    x = 0.0;
				    y = 0.0;
				    z = 0.0;
				    rot = 0.0;
				    MaxSpeed = 0;
				}
				// Store the proper value in the proper place
				if (strcmp(ParameterName, "CamX", false) == 0) // If the parametername is correct ("CamX")
				    x = floatstr(ParameterValue); // Store the CamX
				if (strcmp(ParameterName, "CamY", false) == 0) // If the parametername is correct ("CamY")
				    y = floatstr(ParameterValue); // Store the CamY
				if (strcmp(ParameterName, "CamZ", false) == 0) // If the parametername is correct ("CamZ")
				    z = floatstr(ParameterValue); // Store the CamZ
				if (strcmp(ParameterName, "CamAngle", false) == 0) // If the parametername is correct ("CamAngle")
				    rot = floatstr(ParameterValue); // Store the CamAngle
				if (strcmp(ParameterName, "CamSpeed", false) == 0) // If the parametername is correct ("CamSpeed")
				    MaxSpeed = strval(ParameterValue); // Store the CamSpeed

				// Check if a end of a camera has been found
				if (strcmp(ParameterName, "[/Camera]", false) == 0)
				{
					SetupSpeedCamera(CamID, x, y, z, rot, MaxSpeed); // Setup the camera
					TotalCameras++; // Count the total camera's loaded from the file
					CamID++; // Increase the CamID to select the next camera before data is loaded (in case more camera's are found in the file)
				}
			}

            // Read the next line of the file
			fread(CFile, LineFromFile);
		}

        // Close the file
		fclose(CFile);
	}
}

// This function will save the speedcamera's datafile
CameraFile_Save()
{
	// Setup local variables
	new file[100], File:PFile, LineForFile[100];

    // Save the file
	format(file, sizeof(file), CameraFile); // Construct the complete filename for the camera-file

	PFile = fopen(file, io_write); // Open the camera-file for writing

	// Loop through all camera's
	for (new CamID; CamID < MAX_CAMERAS; CamID++)
	{
		// Check if this index holds a camera
		if (ACameras[CamID][CamSpeed] != 0)
		{
			fwrite(PFile, "[Camera]\r\n"); // Save the header of the camera

			format(LineForFile, 100, "CamX %f\r\n", ACameras[CamID][CamX]);
			fwrite(PFile, LineForFile); // And save it to the file
			format(LineForFile, 100, "CamY %f\r\n", ACameras[CamID][CamY]);
			fwrite(PFile, LineForFile); // And save it to the file
			format(LineForFile, 100, "CamZ %f\r\n", ACameras[CamID][CamZ]);
			fwrite(PFile, LineForFile); // And save it to the file
			format(LineForFile, 100, "CamAngle %f\r\n", ACameras[CamID][CamAngle]);
			fwrite(PFile, LineForFile); // And save it to the file
			format(LineForFile, 100, "CamSpeed %i\r\n", ACameras[CamID][CamSpeed]);
			fwrite(PFile, LineForFile); // And save it to the file

			fwrite(PFile, "[/Camera]\r\n"); // Save the end of this camera
			fwrite(PFile, "\r\n"); // Save an empty line to split the camera's up a bit (for readability)
		}
	}

	fclose(PFile); // Close the file
}



// This function creates a speedcamera (store data and create the objects)
SetupSpeedCamera(CamID, Float:x, Float:y, Float:z, Float:rot, MaxSpeed)
{
	// Store all the given values
    ACameras[CamID][CamX] = x;
    ACameras[CamID][CamY] = y;
    ACameras[CamID][CamZ] = z;
    ACameras[CamID][CamAngle] = rot;
    ACameras[CamID][CamSpeed] = MaxSpeed;
	// Create both camera objects and store their reference
    ACameras[CamID][CamObj1] = CreateDynamicObject(18880, x, y, z, 0.0, 0.0, rot);
    ACameras[CamID][CamObj2] = CreateDynamicObject(18880, x, y, z, 0.0, 0.0, rot + 180.0);

	// Update the draw distance of each camera to 300m, otherwise they could be invisible until you're standing right next to them
	// By default, dynamic objects have a draw distance of 0m, meaning they're only visible when you're very close to them
	Streamer_SetFloatData(STREAMER_TYPE_OBJECT, ACameras[CamID][CamObj1], E_STREAMER_DRAW_DISTANCE, 300.0);
	Streamer_SetFloatData(STREAMER_TYPE_OBJECT, ACameras[CamID][CamObj2], E_STREAMER_DRAW_DISTANCE, 300.0);
}



// ******************************************************************************************************************************
// Support functions
// ******************************************************************************************************************************

// This function is copied from the include-file "dutils.inc"
stock StripNewLine(string[])
{
	new len = strlen(string); // Get the length of the given string

	if (string[0] == 0) return ; // If the given string is empty, exit the function
	if ((string[len - 1] == '\n') || (string[len - 1] == '\r')) // If the string ends with \n or \r
	{
		string[len - 1] = 0; // Replace the \n or \r with a 0 character
		if (string[0]==0) return ; // If the string became empty, exit the function
		if ((string[len - 2] == '\n') || (string[len - 2] == '\r')) // Check again if the string ends with \n or \r
			string[len - 2] = 0; // Replace the \n or \r again with a 0 character
	}
}



// ******************************************************************************************************************************
// Special functions that try to access external public functions to retreive data from another script
// ******************************************************************************************************************************

// This function is used to get the player's money
INT_GetPlayerMoney(playerid)
{
	// Setup local variables
	new Money;

	// Try to call the external function to get the player's money (used to get the serversided money for this player)
	Money = CallRemoteFunction("Admin_GetPlayerMoney", "i", playerid);

	// The external function returned "0" (as the player doesn't have any money yet), or the function is not used in another script
	if (Money == 0)
		return GetPlayerMoney(playerid); // Return the normal money of the player
	else
		return Money; // Return the money that was returned by the external function
}

// This function is used to set the player's money
INT_GivePlayerMoney(playerid, Money)
{
	// Setup local variables
	new Success;

	// Try to call the external function to get the player's money (used to get the serversided money for this player)
	Success = CallRemoteFunction("Admin_GivePlayerMoney", "ii", playerid, Money);

	// The external function returned "0" as the function is not used in another script
	if (Success == 0)
		GivePlayerMoney(playerid, Money); // Use the normal money (client-sided money)
}

// This function checks if the admin-level of a player is sufficient
INT_CheckPlayerAdminLevel(playerid, AdminLevel)
{
	// Setup local variables
	new Level;

	// Check if the player is an RCON admin
	if (IsPlayerAdmin(playerid))
	    return 1; // Return 1 to indicate this player has a sufficient admin-level to use a command

	// If the player is not an RCON admin, try to get his admin-level from an external script using a remote function
	Level = CallRemoteFunction("Admin_GetPlayerAdminLevel", "i", playerid);
	// Check if the player has a sufficient admin-level
	if (Level >= AdminLevel)
	    return 1; // Return 1 to indicate this player has a sufficient admin-level
	else
		return 0; // Return 0 to indicate this player has an insufficient admin-level
}

// This function checks if the player has logged in properly by entering his password
INT_IsPlayerLoggedIn(playerid)
{
	// Setup local variables
	new LoggedIn;

	// Try to determine if the player logged in properly by entering his password in another script
	LoggedIn = CallRemoteFunction("Admin_IsPlayerLoggedIn", "i", playerid);

	// Check if the player has logged in properly
	switch (LoggedIn)
	{
		case 0: return 1; // No admin script present that holds the LoggedIn status of a player, so allow a command to be used
		case 1: return 1; // The player logged in properly by entering his password, allow commands to be used
		case -1: return 0; // There is an admin script present, but the player hasn't entered his password yet, so block all commands
							// This prevents executing the commands using F6 during login with an admin-account before entering a password
	}

	// In any other case, block all commands
	return 0;
}



// ******************************************************************************************************************************
// External functions to be used from within other filterscripts or gamemode (these aren't called anywhere inside this script)
// These functions can be called from other filterscripts or the gamemode to get data from the speedometer filterscript
// ******************************************************************************************************************************

// This function can be used to get the fuel-status from the given vehicle
forward Speedo_GetVehicleFuel(vehicleid);
public Speedo_GetVehicleFuel(vehicleid)
{
	return AVehicleData[vehicleid][Fuel];
}

// This function can be used to set the fuel-status for the given vehicle
forward Speedo_SetVehicleFuel(vehicleid, fuel);
public Speedo_SetVehicleFuel(vehicleid, fuel)
{
	// If a fuel-value of -1 is used, this will refuel the vehicle to maximum fuel
	if (fuel == -1)
	{
		AVehicleData[vehicleid][Fuel] = MaxFuel; // Set fuel to maximum
		return 1; // Return 1 (this can be used in the other script to check if the function was called successfully)
	}

	// Fuel cannot be negative (other negative values are ignored)
	if (fuel >= 0)
	{
		// Check if the fuel is within normal limits
		if (fuel > MaxFuel)
			AVehicleData[vehicleid][Fuel] = MaxFuel; // If a higher value was given than allowed (higher than MaxFuel), set fuel to maximum
		else
			AVehicleData[vehicleid][Fuel] = fuel; // Set the fuel to the given value
	}
	else
	    return -1; // Return -1 (this can be used in the other script to check if the function was called successfully,
					// but the fuel-value was not acceptable)

	// Return 1 (this can be used in the other script to check if the function was called successfully)
	return 1;
}

// This function can be used to get the player's speed (returns 0 if the player is not inside a vehicle)
forward Speedo_GetPlayerSpeed(playerid);
public Speedo_GetPlayerSpeed(playerid)
{
	return APlayerData[playerid][PlayerSpeed];
}



// ******************************************************************************************************************************
// Functions that need to be placed in the gamemode or filterscript which holds the playerdata
// Only needed when the server uses server-sided money, otherwise the normal money is used
// ******************************************************************************************************************************

/*
// This function is used to get the player's money
forward Admin_GetPlayerMoney(playerid);
public Admin_GetPlayerMoney(playerid)
{
	return APlayerData[playerid][PlayerMoney];
}

// This function is used to get the player's money
forward Admin_GivePlayerMoney(playerid, Money);
public Admin_GivePlayerMoney(playerid, Money)
{
	// Add the given money to the player's account
	APlayerData[playerid][PlayerMoney] = APlayerData[playerid][PlayerMoney] + Money;

	// Return that the function had success (another script holds the player's money on the server-side)
	return 1;
}

// This function is used to get the player's admin-level
forward Admin_GetPlayerAdminLevel(playerid);
public Admin_GetPlayerAdminLevel(playerid)
{
	return APlayerData[playerid][AdminLevel];
}

// This function is used to determine if the player has logged in (he succesfully entered his password)
forward Admin_IsPlayerLoggedIn(playerid);
public Admin_IsPlayerLoggedIn(playerid)
{
	if (APlayerData[playerid][LoggedIn] == true)
	    return 1; // The player has logged in succesfully
	else
	    return -1; // The player hasn't logged in (yet)
}
*/

